package org.eclipse.epsilon.emc.jdt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.epsilon.common.util.StringProperties;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter;
import org.eclipse.epsilon.eol.execute.introspection.IPropertySetter;
import org.eclipse.epsilon.eol.models.Model;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;

/**
 * This is the JDT model class.
 * @author Cheng Yun
 *
 */
public class JdtModel extends Model {
	private List<String> supportedTypes = Arrays.asList("JavaProject",
			"Package");
	private String[] projectsSelected;
	private List<IJavaProject> javaProjects = new ArrayList<IJavaProject>();
	private ASTReflection astModel;

	@Override
	public void load() throws EolModelLoadingException {
		
	}

	@Override
	public void load(StringProperties properties, String basePath)
			throws EolModelLoadingException {
		super.load(properties, basePath);
		String projects = properties.getProperty(JdtModelConfig.PROJECTS);
		if(projects.length()!=0){
			projectsSelected = projects.split(",");
		}
		getJavaProject(projectsSelected);
		try {
			String str = properties.getProperty(JdtModelConfig.MODELS);
			if (str != "") {
				int temp = Integer.parseInt(properties.getProperty(JdtModelConfig.MODELS));
				//dynamic binding
				switch (temp) {
				case JdtModelConfig.RUNTIME_PARSING:
					astModel = new ReflectiveASTVisitor(javaProjects);
					break;
				case JdtModelConfig.ASTMODEL:
					astModel = new ASTModel(javaProjects);
					break;
				case JdtModelConfig.ASTMODEL_GC:
					//TODO 
					astModel = new ASTModel(javaProjects);
					break;
				default:
					astModel = new ReflectiveASTVisitor(javaProjects);
					break;
				}
			}else{
				astModel = new ReflectiveASTVisitor(javaProjects);
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public Object getEnumerationValue(String enumeration, String label)
			throws EolEnumerationValueNotFoundException {
		return null;
	}

	@Override
	public Collection<?> allContents() {
		try {
			return astModel.getAllOfKind("ComopilationUnit");
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Collection<?> getAllOfType(String type)
			throws EolModelElementTypeNotFoundException {
	
		//get java projects
		if (type.equals("JavaProject")) {
			return javaProjects;
		}
		
		// get packages
		if (type.equals("Package")) {
			List<IPackageFragment> packages = new ArrayList<IPackageFragment>();
			try {
				// get packages
				packages.addAll(JdtManipulator
						.getIPackageFragments(javaProjects, true));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return packages;
		}

		try {
			return astModel.getAllOfType(type);
		} catch (JavaModelException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Collection<?> getAllOfKind(String type)
			throws EolModelElementTypeNotFoundException {
		//get java projects
		if (type.equals("JavaProject")) {
			return javaProjects;
		}
		
		// get packages
		if (type.equals("Package")) {
			List<IPackageFragment> packages = new ArrayList<IPackageFragment>();
			try {
				// get packages
				packages.addAll(JdtManipulator
						.getIPackageFragments(javaProjects, true));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return packages;
		}
		
		try {
			return astModel.getAllOfKind(type);
		} catch (JavaModelException e) {
			throw new RuntimeException(e);
		}
		
		//return getAllOfType(type);
	}

	@Override
	public String getTypeNameOf(Object instance) {
		if(instance instanceof IJavaProject){
			return IJavaProject.class.toString();
		}
		
		if(instance instanceof IPackageFragment){
			return IJavaProject.class.toString();
		}
		
		if(instance instanceof ASTNode){
			ASTNode node = (ASTNode)instance;
			return ASTNode.nodeClassForType(node.getNodeType()).toString();
		}
		
		return null;
	}

	@Override
	public boolean isOfType(Object instance, String metaClass)
			throws EolModelElementTypeNotFoundException {
		return instance.getClass().getSimpleName().equals(metaClass);
	}
	
	@Override
	public Object createInstance(String type)
			throws EolModelElementTypeNotFoundException,
			EolNotInstantiableModelElementTypeException {

		return null;
	}

	@Override
	public Object getElementById(String id) {

		return null;
	}

	@Override
	public String getElementId(Object instance) {

		return null;
	}

	@Override
	public void setElementId(Object instance, String newId) {

	}

	@Override
	public void deleteElement(Object instance) throws EolRuntimeException {

	}

	@Override
	public boolean owns(Object instance) {
		if (instance instanceof IJavaElement || instance instanceof ASTNode) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isInstantiable(String type){
		return true;
	}

	@Override
	public boolean hasType(String type) {
		try {
			return supportedTypes.contains(type) || (Class.forName("org.eclipse.jdt.core.dom." + type) != null);
		} catch (ClassNotFoundException e) {
			return false;
		}
	}

	@Override
	public boolean store(String location) {
		return false;
	}

	@Override
	public boolean store() {
		return false;
	}

	@Override
	public IPropertySetter getPropertySetter() {
		return new JdtPropertySetter();
	}

	@Override
	public IPropertyGetter getPropertyGetter() {
		return new JdtPropertyGetter();
	}
	
	private List <IJavaProject> getJavaProject (String[] projectsName){
		List<IProject> projects;
		if(projectsName==null){
			//get all projects in the workspace including non-Java projects
			projects = JdtManipulator.getIProjects();
		}else{
			//get selected projects in the workspace
			projects = JdtManipulator.getIProjects(projectsName);
		}
		try {
			//get java projects
			javaProjects.addAll(JdtManipulator.getIJavaProjects(projects));
		} catch (CoreException e) {
			e.printStackTrace();
		}
		return javaProjects;
	}
}
